﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using FilterData;
using Common;

namespace LoginServer
{
    public class MachineConnection
    {
        #region Server
        Socket sock;
        public IPAddress ipServer;
        public int portServer;
        #endregion

        #region machine
        byte machine_id;
        Socket machine;
        IPAddress ipmachine;

        bool isStart;

        byte[] buffer = new byte[1024];
        byte[] dataSend;
        byte[] dataReceive;
        FilterDataManager filters;
        #endregion

        public MachineConnection(string ip, int port,string ipmachine)
        {
            this.ipServer = IPAddress.Parse(ip);
            this.portServer = port;
            
            
            this.ipmachine = IPAddress.Parse(ipmachine);
            IPEndPoint endpoint = new IPEndPoint(ipServer,portServer);
            try
            {
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sock.Bind(endpoint);
            }
            catch(Exception e)
            {

            }

            Init();
        }

        public void Init()
        {          
            isStart = false;
            filters = new FilterDataManager();
        }

        public void Start()
        {
            isStart = true;
            sock.Listen(1);
            sock.BeginAccept(new AsyncCallback(AcceptUser), sock);        
            Print("Port Machine Start in Port = "+portServer.ToString());            
        }

        void AcceptUser(IAsyncResult ar)
        {
            try
            {
                if(!ar.IsCompleted)
                {
                    sock.BeginAccept(new AsyncCallback(AcceptUser), sock);
                    return;
                }
                machine = sock.EndAccept(ar);
                IPEndPoint endpoint = (IPEndPoint)machine.RemoteEndPoint;
                if(!endpoint.Address.ToString().Equals(ipmachine.ToString()))
                {
                    machine.Disconnect(true);
                    machine = null;
                    sock.BeginAccept(new AsyncCallback(AcceptUser), sock);
                    return;
                }
                Print("Machine at Port=" + portServer.ToString() + " Connected with " + endpoint.Address.ToString()+":"+endpoint.Port.ToString());
                machine.BeginReceive(buffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), machine);

            }
            catch (System.Exception e)
            {
            	
            }
        }

        #region Receive Data

        int SumSizeReceive = 0;
        int SizeReceived = 0;
        void OnSizeReceive(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted)
                {
                    Console.WriteLine("Send Size Failed");
                    return;
                }
                machine.EndReceive(ar);              
                SumSizeReceive = buffer[0];
                if (machine.Connected)
                {
                    if (SumSizeReceive > 0)                  
                        machine.BeginReceive(buffer, 2, SumSizeReceive, SocketFlags.None, new AsyncCallback(OnReceive),machine);
                    else
                        machine.BeginReceive(buffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), machine);                     
                }
            }
            catch (System.Exception e)
            {
                machineDisconnect();
            }
        }
        void OnReceive(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted)
                {
                    Console.WriteLine("Receive Error");
                    return;
                }
                SocketError error;
                int i = machine.EndReceive(ar, out error);
                
                if (i < 1)
                {
                    Console.WriteLine("Receive Error");
                    return;
                }
                SizeReceived += i;
                if (machine.Connected)
                {
                    if (SizeReceived < SumSizeReceive)
                        machine.BeginReceive(buffer, SizeReceived + 2, SumSizeReceive - SizeReceived, SocketFlags.None, new AsyncCallback(OnReceive), machine);
                                         
                    else
                    {
                        ProcessReceiveFullData();
                        ResetSumReceive();
                        machine.BeginReceive(buffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), machine);
                    }
                }



            }
            catch (System.Exception e)
            {
                machineDisconnect();
            }
        }
        void ProcessReceiveFullData()
        {
            dataReceive = filters.ProcessDataReceive(buffer);
            if (MachineReceive != null)
                MachineReceive(machine, new MachineReceiveEventArgs(dataReceive, this));
        }

        void ResetSumReceive()
        {
            SumSizeReceive = 0;
            SizeReceived = 0;
            buffer = new byte[1024];
        }
        #endregion

        #region Send Data
        void SendData(byte[] data)
        {          
            dataSend = filters.ProcessDataSend(data);
            if (machine.Connected)
                machine.BeginSend(dataSend, 0, dataSend.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
            else
                Print("Machine " + ipmachine.ToString() + " Disconnected");
        }

        public void SendMessage(MSG_GSM message, params object[] param)
        {
            //Console.WriteLine("Send Message: " + message);
            List<byte> Data = new List<byte>();
            Data.Add((byte)message);
            foreach (object obj in param)
            {
                Data.Add((byte)obj);
            }
            SendData(Data.ToArray());
        }

        public void SendMessage(MSG_GSM message,byte[] param)
        {
            //Console.WriteLine("Send Message: " + message);
            List<byte> Data = new List<byte>();
            Data.Add((byte)message);
            Data.AddRange(param);
            SendData(Data.ToArray());
        }


        void OnSend(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted || !machine.Connected)
                    Console.WriteLine("Send no complete");
                machine.EndSend(ar);              
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        #endregion
       

        void Print(string text)
        {
            Console.WriteLine(text);
        }
        void machineDisconnect()
        {
            machine.Disconnect(true);           
            sock.Close();
            sock = null;
            if (MachineDisconnect != null)
                MachineDisconnect(machine, new MachineDisconnectEventArgs(this));
        }

        public byte Machine_ID
        {
            get { return machine_id; }
            set { machine_id = value; }
        }
        

        public event EventHandler<MachineDisconnectEventArgs> MachineDisconnect;
        public event MachineReceiveHandle MachineReceive;
        public delegate void MachineReceiveHandle(object sender, MachineReceiveEventArgs args);

    }
}
